From f87464520564ff2eb0f5636f3bc4cd36952b7ef8 Mon Sep 17 00:00:00 2001 From: =?utf8?q?David=20H=C3=A4rdeman?= Date: Thu, 25 Sep 2025 17:09:08 +0200 Subject: [PATCH] dhcpv4: preparations for iovec usage MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit dhcpv4.c currently builds DHCPv4 messages by lots of repeated calls to the dhcpv4_put() method, which basically does a whole lot of copies to the stack. The following set of patches convert dhcpv4.c over to use iovecs, which avoids almost all of the stack allocations and copies. The first step is to lay the groundwork by changing dhcpv4_send_reply() and the corresponding dhcpv6_4o6_send_reply() in dhcpv6.c so that they both take an iovec instead of a plain buffer. Also introduce the first (very very simple) iovec for dhcpv4_handle_msg() which just contains the whole packet, plus the end marker as a separate vector element. It will be filled out further in subsequent patches. Signed-off-by: David Härdeman Link: https://github.com/openwrt/odhcpd/pull/278 Signed-off-by: Álvaro Fernández Rojas --- src/dhcpv4.c | 70 ++++++++++++++++++++++++++++++++-------------------- src/dhcpv6.c | 25 +++++++++++++------ src/odhcpd.h | 6 ++--- 3 files changed, 63 insertions(+), 38 deletions(-) diff --git a/src/dhcpv4.c b/src/dhcpv4.c index dcb2373..3d2ad98 100644 --- a/src/dhcpv4.c +++ b/src/dhcpv4.c @@ -549,39 +549,33 @@ dhcpv4_lease(struct interface *iface, enum dhcpv4_msg msg, const uint8_t *mac, return a; } -static int dhcpv4_send_reply(const void *buf, size_t len, - const struct sockaddr *dest, socklen_t dest_len, - void *opaque) +static ssize_t dhcpv4_send_reply(struct iovec *iov, size_t iov_len, + struct sockaddr *dest, socklen_t dest_len, + void *opaque) { int *sock = opaque; + struct msghdr msg = { + .msg_name = dest, + .msg_namelen = dest_len, + .msg_iov = iov, + .msg_iovlen = iov_len, + }; - return sendto(*sock, buf, len, MSG_DONTWAIT, dest, dest_len); + return sendmsg(*sock, &msg, MSG_DONTWAIT); } +enum { + IOV_HEADER = 0, + IOV_END, + IOV_TOTAL +}; + void dhcpv4_handle_msg(void *addr, void *data, size_t len, struct interface *iface, _unused void *dest_addr, send_reply_cb_t send_reply, void *opaque) { struct dhcpv4_message *req = data; - - if (iface->dhcpv4 == MODE_DISABLED) - return; - - /* FIXME: would checking the magic cookie value here break any clients? */ - - if (len < offsetof(struct dhcpv4_message, options) || - req->op != DHCPV4_OP_BOOTREQUEST || req->hlen != ETH_ALEN) - return; - - debug("Got DHCPv4 request on %s", iface->name); - - if (!iface->dhcpv4_start_ip.s_addr && !iface->dhcpv4_end_ip.s_addr) { - warn("No DHCP range available on %s", iface->name); - return; - } - int sock = iface->dhcpv4_event.uloop.fd; - struct dhcpv4_message reply = { .op = DHCPV4_OP_BOOTREPLY, .htype = req->htype, @@ -595,7 +589,12 @@ void dhcpv4_handle_msg(void *addr, void *data, size_t len, .siaddr = iface->dhcpv4_local, .cookie = htonl(DHCPV4_MAGIC_COOKIE), }; - memcpy(reply.chaddr, req->chaddr, sizeof(reply.chaddr)); + uint8_t end_opt = DHCPV4_OPT_END; + + struct iovec iov[IOV_TOTAL] = { + [IOV_HEADER] = { &reply, 0 }, + [IOV_END] = { &end_opt, sizeof(end_opt) }, + }; uint8_t *cursor = reply.options; uint8_t reqmsg = DHCPV4_MSG_REQUEST; @@ -614,6 +613,24 @@ void dhcpv4_handle_msg(void *addr, void *data, size_t len, uint8_t *end = ((uint8_t *)data) + len; struct dhcpv4_option *opt; + if (iface->dhcpv4 == MODE_DISABLED) + return; + + /* FIXME: would checking the magic cookie value here break any clients? */ + + if (len < offsetof(struct dhcpv4_message, options) || + req->op != DHCPV4_OP_BOOTREQUEST || req->hlen != ETH_ALEN) + return; + + memcpy(reply.chaddr, req->chaddr, sizeof(reply.chaddr)); + + debug("Got DHCPv4 request on %s", iface->name); + + if (!iface->dhcpv4_start_ip.s_addr && !iface->dhcpv4_end_ip.s_addr) { + warn("No DHCP range available on %s", iface->name); + return; + } + dhcpv4_for_each_option(start, end, opt) { if (opt->type == DHCPV4_OPT_MESSAGE && opt->len == 1) reqmsg = opt->data[0]; @@ -865,8 +882,6 @@ void dhcpv4_handle_msg(void *addr, void *data, size_t len, } } - dhcpv4_put(&reply, &cursor, DHCPV4_OPT_END, 0, NULL); - struct sockaddr_in dest = *((struct sockaddr_in*)addr); if (req->giaddr.s_addr) { /* @@ -914,8 +929,9 @@ void dhcpv4_handle_msg(void *addr, void *data, size_t len, } } - if (send_reply(&reply, PACKET_SIZE(&reply, cursor), - (struct sockaddr*)&dest, sizeof(dest), opaque) < 0) + iov[IOV_HEADER].iov_len = PACKET_SIZE(&reply, cursor); + + if (send_reply(iov, ARRAY_SIZE(iov), (struct sockaddr *)&dest, sizeof(dest), opaque) < 0) error("Failed to send %s to %s - %s: %m", dhcpv4_msg_to_string(msg), dest.sin_addr.s_addr == INADDR_BROADCAST ? diff --git a/src/dhcpv6.c b/src/dhcpv6.c index abb0f36..8ce1bff 100644 --- a/src/dhcpv6.c +++ b/src/dhcpv6.c @@ -253,21 +253,30 @@ struct dhcpv4_msg_data { ssize_t len; }; -static int send_reply(_unused const void *buf, size_t len, - _unused const struct sockaddr *dest, _unused socklen_t dest_len, - _unused void *opaque) +static ssize_t dhcpv6_4o6_send_reply(struct iovec *iov, size_t iov_len, + _unused struct sockaddr *dest, + _unused socklen_t dest_len, + void *opaque) { struct dhcpv4_msg_data *reply = opaque; + size_t len = 0; + + for (size_t i = 0; i < iov_len; i++) + len += iov[i].iov_len; if (len > reply->maxsize) { error("4o6: reply too large, %zu > %zu", len, reply->maxsize); reply->len = -1; - } else { - memcpy(reply->msg, buf, len); - reply->len = len; + return -1; + } + + for (size_t i = 0, off = 0; i < iov_len; i++) { + memcpy(reply->msg + off, iov[i].iov_base, iov[i].iov_len); + off += iov[i].iov_len; } + reply->len = len; - return reply->len; + return len; } static ssize_t dhcpv6_4o6_query(uint8_t *buf, size_t buflen, @@ -301,7 +310,7 @@ static ssize_t dhcpv6_4o6_query(uint8_t *buf, size_t buflen, addrv4.sin_port = htons(DHCPV4_CLIENT_PORT); dhcpv4_handle_msg(&addrv4, msgv4_data, msgv4_len, - iface, NULL, send_reply, &reply); + iface, NULL, dhcpv6_4o6_send_reply, &reply); return reply.len; } diff --git a/src/odhcpd.h b/src/odhcpd.h index dc77c03..dd2eb82 100644 --- a/src/odhcpd.h +++ b/src/odhcpd.h @@ -88,9 +88,9 @@ struct odhcpd_event { void (*recv_msgs)(struct odhcpd_event *e); }; -typedef int (*send_reply_cb_t)(const void *buf, size_t len, - const struct sockaddr *dest, socklen_t dest_len, - void *opaque); +typedef ssize_t (*send_reply_cb_t)(struct iovec *iov, size_t iov_len, + struct sockaddr *dest, socklen_t dest_len, + void *opaque); typedef void (*dhcpv6_binding_cb_handler_t)(struct in6_addr *addr, int prefix, uint32_t pref, uint32_t valid, -- 2.30.2